package ziputil

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"

	Log "github.com/cihub/seelog"
)

/**
 * 打包成zip文件
 * src 源文件夹
 * target 目标文件名
 */
func Create(source string, target string) (bool, string, interface{}) {
	source = strings.TrimSpace(source)

	if _, err := os.Stat(source); err != nil {
		if os.IsNotExist(err) {
			return false, "源文件不存在", 1001
		}

		return false, "访问源文件异常:" + err.Error(), 1002
	}

	target = strings.TrimSpace(target)

	os.MkdirAll(path.Dir(target), os.ModePerm) //创建多级目录

	os.RemoveAll(target) // 预防：旧文件无法覆盖

	targetFile, err := os.Create(target) // 创建目标 zip 文件
	if err != nil {
		Log.Error("创建目标zip文件失败:", err.Error())
		return false, "创建目标zip文件失败", 1003
	}

	defer targetFile.Close()

	// 创建 zip.Writer
	zipWriter := zip.NewWriter(targetFile)
	defer zipWriter.Close()

	var me []interface{}
	// 获取需要打包的所有文件
	filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			Log.Error("打包zip文件失败:", err.Error())
			me = []interface{}{false, "打包zip文件失败", 1004}
			return err
		}

		// 如果是源路径，提前进行下一个遍历
		if path == source {
			return nil
		}

		// 创建新的 zip 文件头信息
		header, err := zip.FileInfoHeader(info)
		if err != nil {
			Log.Error("创建新的zip文件头信息失败:", err.Error())
			me = []interface{}{false, "创建新的zip文件头信息失败", 1005}
			return err
		}

		// 设置文件头信息中的 Name 字段
		header.Name, err = filepath.Rel(source, path)
		if err != nil {
			return err
		}

		// zip.Store | zip.Deflate 预定义压缩算法。
		// archive/zip包中预定义的有两种压缩方式。一个是仅把文件写入到zip中。不做压缩。一种是压缩文件然后写入到zip中。默认的Store模式。就是只保存不压缩的模式。
		// Store   unit16 = 0  //仅存储文件
		// Deflate unit16 = 8  //压缩文件

		// 判断当前路径是否为一个文件夹
		if info.IsDir() {
			// 如果是文件夹，创建文件夹信息
			header.Name += "/"
			header.Method = zip.Store //文件夹不压缩
		} else {
			// 如果是文件，打开文件准备读取内容,并使用压缩
			header.Method = zip.Deflate
		}

		header.Name = strings.Replace(header.Name, "\\", "/", -1)

		// 将新的文件头信息写入到 zip.Writer 中
		writer, err := zipWriter.CreateHeader(header)
		if err != nil {
			Log.Error("新的文件头信息写入失败:", err.Error())
			me = []interface{}{false, "新的文件头信息写入失败", 1006}
			return err
		}

		// 如果当前路径是一个文件，读取文件内容并写入 zip.Writer 中
		if !info.IsDir() {
			file, err := os.Open(path)
			if err != nil {
				Log.Error("指定文件不存在:", err.Error())
				me = []interface{}{false, "指定文件不存在", 1007}
				return err
			}
			defer file.Close()

			_, err = io.Copy(writer, file)
			if err != nil {
				Log.Error("复制文件失败:", err.Error())
				me = []interface{}{false, "复制文件失败", 1008}
				return err
			}
		}

		return nil
	})

	if me != nil && !me[0].(bool) {
		return me[0].(bool), me[1].(string), me[2]
	}

	return true, "打包结束", 1999
}

/**
 * 读取指定(序号)文件
 * source 源文件
 * Index 序号
 * (bool, string, interface{}) (content []byte, fileName string)
 */
func ReadByIndex(source string, Index int) (bool, string, interface{}) {
	if Index < 0 {
		return false, "下标越界,最小下标:0", 1001
	}

	source = strings.TrimSpace(source)

	if _, err := os.Stat(source); err != nil {
		if os.IsNotExist(err) {
			return false, "源文件不存在", 1002
		}

		return false, "访问源文件异常:" + err.Error(), 1003
	}

	zipFile, err := zip.OpenReader(source) // 打开ZIP文件
	if err != nil {
		return false, "打开zip文件异常:" + err.Error(), 1004
	}
	defer zipFile.Close()

	if Index >= len(zipFile.File) {
		return false, fmt.Sprintf("下标越界,最大下标:%v", (len(zipFile.File) - 1)), 1005
	}

	file := zipFile.File[Index]
	f, err := file.Open() // 打开ZIP文件内的成员
	if err != nil {
		return false, "读取文件异常:" + err.Error(), 1006
	}
	defer f.Close()

	// 读取ZIP内文件的内容
	buf := new(bytes.Buffer)
	if _, err := io.Copy(buf, f); err != nil {
		return false, "读取文件内容异常:" + err.Error(), 1007
	}

	return true, file.Name, buf.Bytes()
}

/**
 * 读取压缩文件里的文件数
 * source 源文件
 * Index 序号
 * (bool, string, interface{}) (content []byte, fileName string)
 */
func LoadCount(source string) (bool, string, interface{}) {
	source = strings.TrimSpace(source)

	if _, err := os.Stat(source); err != nil {
		if os.IsNotExist(err) {
			return false, "源文件不存在", 1001
		}

		return false, "访问源文件异常:" + err.Error(), 1002
	}

	zipFile, err := zip.OpenReader(source) // 打开ZIP文件
	if err != nil {
		return false, "打开zip文件异常:" + err.Error(), 1003
	}
	defer zipFile.Close()

	iCount := len(zipFile.File)

	return true, "数量读取成功", iCount
}

/**
 * 读取指定(名称)文件
 * source 源文件
 * Name 名称
 * (bool, string, interface{}) (content []byte, fileName string)
 */
func ReadByName(source, Name string) (bool, string, interface{}) {
	source = strings.TrimSpace(source)

	if _, err := os.Stat(source); err != nil {
		if os.IsNotExist(err) {
			return false, "源文件不存在", 1001
		}

		return false, "访问源文件异常:" + err.Error(), 1002
	}

	zipFile, err := zip.OpenReader(source) // 打开ZIP文件
	if err != nil {
		return false, "打开zip文件异常:" + err.Error(), 1003
	}
	defer zipFile.Close()

	Name = strings.TrimSpace(Name)

	// 遍历ZIP文件内的各个文件
	var content []byte
	var fileName string
	for _, file := range zipFile.File {
		if file.Name == Name {
			continue
		}

		f, err := file.Open() // 打开ZIP文件内的成员
		if err != nil {
			return false, "读取文件异常:" + err.Error(), 1004
		}
		defer f.Close()

		// 读取ZIP内文件的内容
		buf := new(bytes.Buffer)
		if _, err := io.Copy(buf, f); err != nil {
			return false, "读取文件内容异常:" + err.Error(), 1005
		}

		fileName = file.Name
		content = buf.Bytes()

		break
	}

	if fileName == "" {
		return false, "未找到文件:" + Name, 1006
	}

	return true, fileName, content
}

/**
 * 解压zip文件
 * src 源文件夹
 * target 目标文件夹
 * (bool, string, interface{}) (int, msg)
 */
func UnZip(source string, target string) (bool, string, interface{}) {
	if _, err := os.Stat(source); err != nil {
		if os.IsNotExist(err) {
			return false, "源文件不存在", 1001
		}

		return false, "访问源文件异常:" + err.Error(), 1002
	}

	// 打开Zip文件
	r, err := zip.OpenReader(source)
	if err != nil {
		return false, "打开zip文件异常:" + err.Error(), 1003
	}
	defer r.Close()

	//target = strings.TrimSpace(target)

	// 遍历Zip文件中的所有文件并解压缩
	iCount := 0
	for _, f := range r.File {
		rc, err := f.Open() // 打开文件
		if err != nil {
			return false, "读取文件异常:" + err.Error(), 1004
		}
		defer rc.Close()

		dst, err := os.Create(f.Name) // 创建目标文件
		if err != nil {
			return false, "创建目标文件异常:" + err.Error(), 1005
		}
		defer dst.Close()

		_, err = io.Copy(dst, rc) // 将文件内容复制到目标文件中
		if err != nil {
			return false, "取源文件内容到目标文件异常:" + err.Error(), 1006
		}

		iCount++
	}

	if iCount < len(r.File) {
		return false, fmt.Sprintf("解压结束:%v/%v", iCount, len(r.File)), 1007
	}

	return true, "解压完毕", 9999
}
